/* bytechunk implementation */

#include <string>
#include "bytechunk.h"
#include "ast.h"
#include "exception.h"

using std::string;
using std::vector;

ByteChunk::ByteChunk()
{
	pos = 0;
}


void ByteChunk::SetBaseAddress(unsigned int adr) {
	baseaddress = adr;
}

unsigned int ByteChunk::GetPos() const
{
	return pos;
}

unsigned int ByteChunk::GetSize() const
{
	return bytes.size();
}

void ByteChunk::Byte(unsigned int n)
{
	pos++;
	bytes.push_back((unsigned char)n);
	cinfo.push_back(false);
}

void ByteChunk::Char(unsigned int n)
{
	Byte(n + 0x30);
	cinfo[pos-1] = true;
}

void ByteChunk::Short(unsigned int n)
{
	Byte(n & 255);
	Byte((n >> 8) & 255);
}

void ByteChunk::Long(unsigned int n)
{
	Byte(n & 255);
	Byte((n >> 8) & 255);
	Byte((n >> 16) & 255);
	Byte((n >> 24) & 255);
}

void ByteChunk::Code(const string &code)
{
	unsigned int i = 0;
	char b[3];
	while(i < code.length()) {
		if(code[i] == ' ') { i++; continue; }
		b[0] = code[i++];
		b[1] = code[i++];
		b[2] = 0;
		int n = strtoul(b, NULL, 16);
		Byte(n);
	}
}

void ByteChunk::Truncate(unsigned int newsize)
{
	if(newsize > bytes.size()) return;
	bytes.resize(newsize);

	pos = bytes.size();
	
	cinfo.resize(newsize);
}

void ByteChunk::AddReference(unsigned int location, Label *target)
{
	Reference r;
	r.location = location;
	r.target = target;
	r.offset = 0;
	r.length = 4;
	refs.push_back(r);
}

void ByteChunk::AddReference(unsigned int location, int offset, int length, Label* target)
{
	Reference r;
	r.location = location;
	r.target = target;
	r.offset = offset;
	r.length = length;
	refs.push_back(r);
}

vector<ByteChunk::Reference> ByteChunk::GetReferencesInRange(unsigned int start, unsigned int size) const
{
	vector<Reference> results;
	for(unsigned int i = 0; i < refs.size(); ++i) {
		// A reference is "in" the range if any part of it overlaps;
		// e.g., its pos is in [start-3,start+size+3] (assuming 4-byte references)
		Reference r = refs[i];
		if((signed)r.location >= (signed)(start - 3) && r.location <= start + size + 3)
			results.push_back(r);
	}
	return results;
}

/*
 * Writes the final addresses of all label references into the chunk.
 * Do not call until all label addresses have been computed. :-)
 */
void ByteChunk::ResolveReferences()
{
	try {
		for(unsigned int i = 0; i < refs.size(); ++i) {
			// Get the target address
			unsigned int adr = refs[i].target->GetTarget();
			// Write it to the reference location
			int loc = refs[i].location;
			int offset = refs[i].offset;
			int count = refs[i].length;

			for(int j = offset; j < offset + count; ++j)
				bytes.at(loc + j) = (adr >> (j*8)) & 255;
		}
	}
	catch(...)
	{
		throw Exception("Oopsie");
	}
}

/*
 * Data reading methods
 */
unsigned char ByteChunk::ReadByte(unsigned int pos) const
{
	try {
		return bytes.at(pos);
	}
	catch(std::exception e) {}
	return 0;
}

unsigned short ByteChunk::ReadShort(unsigned int pos) const
{
	unsigned short result = 0;
	try {
		result += bytes.at(pos);
		result += bytes.at(pos+1) << 8;
	}
	catch(std::exception e) {
		// just give incomplete results on exception
	}
	return result;
}

unsigned int ByteChunk::ReadLong(unsigned int pos) const
{
	unsigned long result = 0;
	try {
		result += bytes.at(pos);
		result += bytes.at(pos+1) << 8;
		result += bytes.at(pos+2) << 16;
		result += bytes.at(pos+3) << 24;
	}
	catch(std::exception e) {
	}
	return result;
}

/*
 * Writes the chunk to a specified buffer.
 *
 * NOTE: "location" is assumed to be a virtual address that needs
 * to be mapped to a physical address within the buffer.
 * Returns false if a write was attempted past the end of the buffer.
 */
bool ByteChunk::WriteChunk(char* buffer, int location, int bufsize) const
{
	for(unsigned int i = 0; i < bytes.size(); ++i) {
		int a = location + i;
		if(a >= bufsize)
			return false;
		buffer[a] = bytes[i];
	}
	return true;
}

